package net.ubinow.mflex.events;

import java.util.Hashtable;

import net.ubinow.mflex.data.IDataItem;

public abstract class AbsEventPublisher implements IEventPublisher {
	protected Hashtable _subscribers = null;
    
    public void addEventSubscriber( int event, IEventSubscriber subscriber ){

        String key = Integer.toString(event);
        
        if( _subscribers == null )
        	_subscribers = new Hashtable(2);
        
        if( _subscribers.contains( key )) {
        	IEventSubscriber[] subscribers = (IEventSubscriber[])_subscribers.get( key );
            int oldLen = subscribers.length;
            
            // make sure we don't already have it
            for( int i=0; i<oldLen; i++ ){
                if( subscribers[i] == subscriber )
                    return;
            }
            
            IEventSubscriber[] newSubscribers = new IEventSubscriber[oldLen+1];
            System.arraycopy(subscribers, 0, newSubscribers, 0, oldLen);
            newSubscribers[oldLen] = subscriber;
            _subscribers.put( key, newSubscribers );
        } else{
        	_subscribers.put( key, new IEventSubscriber[]{subscriber} );
        }
    }
    
    public void removeEventSubscriber( int event, IEventSubscriber subscriber ){
        if( _subscribers == null )
            return;
        
        String key = Integer.toString(event);
        if( _subscribers.contains( key )) {
        	IEventSubscriber[] listeners = (IEventSubscriber[])_subscribers.get( key );
            int oldLen = listeners.length;
            
            // make sure we don't already have it
            int indexFound = -1;
            
            for( int i=0; i<oldLen; i++ ){
                if( listeners[i] == subscriber ){
                    indexFound = i;
                    break;
                }
            }
            
            if( indexFound != -1 ){
            	IEventSubscriber[] newListeners = new IEventSubscriber[oldLen-1];
                int index = 0;
                for( int i=0; i<oldLen; i++ ){
                    if( i != indexFound ){
                        newListeners[index] = listeners[i];
                        index++;
                    }
                }
                
                _subscribers.put( key, newListeners );
            }
        }
    }
    
    public void notifySubscribers( Event evt ){
        int count = 0; // counter of the listeners we have dispatched this event too
        int listening = 0; // counter of how many event listeners are actively listening - if not equal to the length of the array then remove the reference to the listeners
        
        if( _subscribers == null ){
            evt.release();
            return;
        }
        
        String key = Integer.toString(evt._type);
        if( _subscribers.containsKey( key )) {
            
        	IEventSubscriber[] subscribers = (IEventSubscriber[])_subscribers.get( key );
            int len = subscribers.length;
            
            for( int i=0; i<len; i++ ){
                if( subscribers[i] != null && subscribers[i].isActiveSubscriber() ){
                	subscribers[i].handleEvent( evt );
                    listening++; 
                    count++;
                }                                
            }
            
            // remove any listeners that maybe null or inactive 
            if( listening == 0 ){
            	_subscribers.remove( key );
            }
            else if( listening < len ){
                int j = 0;
                IEventSubscriber[] newListeners = new IEventSubscriber[listening];
                for( int i=0; i<len; i++ ){
                    if( subscribers[i] != null && subscribers[i].isActiveSubscriber() ){
                        newListeners[j] = subscribers[i];
                        j++; 
                    }
                }                
                _subscribers.put( key, newListeners );                
            }
        } else{
            key = Integer.toString( Event.EVT_ANY );
            if( _subscribers.containsKey( key )) {
                
            	IEventSubscriber[] subscribers = (IEventSubscriber[])_subscribers.get( key );
                int len = subscribers.length;
                
                for( int i=0; i<len; i++ ){
                    if( subscribers[i] != null && subscribers[i].isActiveSubscriber() ){
                    	subscribers[i].handleEvent( evt );
                        listening++; 
                        count++;
                    } 
                }
                
                // remove any listeners that maybe null or inactive 
                if( listening == 0 ){
                	_subscribers.remove( key );
                }
                else if( listening < len ){
                    int j = 0;
                    IEventSubscriber[] newObservers = new IEventSubscriber[listening];
                    for( int i=0; i<len; i++ ){
                        if( subscribers[i] != null && subscribers[i].isActiveSubscriber() ){
                        	newObservers[j] = subscribers[i];
                            j++; 
                        }
                    }                
                    _subscribers.put( key, newObservers );                
                }
            }
        }
        
        if( count == 0 )
            evt.release();
    }            
    
    //<editor-fold defaultstate="collapsed" desc="protected methods">
    
    protected Event getEvet( int type ){
        Event evt = EventFactory.getInstance().getEvent( type );
        evt._type = type;
        evt._publisher = this;
        
        return evt; 
    }
    
    protected Event getEvet( int type, IDataItem item ){
        Event evt = EventFactory.getInstance().getEvent( type );
        evt._publisher = this;
        evt._type = type;
        evt._value = new IDataItem[]{item}; 
        
        return evt; 
    }
    
    protected Event getEvet( int type, IDataItem[] items ){
        Event evt = EventFactory.getInstance().getEvent( type );
        evt._publisher = this;
        evt._type = type;
        evt._value = items; 
        
        return evt; 
    }
    
    protected Event getEvet( int type, Object item ){
        Event evt = EventFactory.getInstance().getEvent( type );
        evt._publisher = this;
        evt._type = type;
        evt._value = item; 
        
        return evt; 
    }

}
